////////////////////////////////////////////////////////////////////////////////
//                                                                            //
//  Filename:    FunkyMonkeyGuts.cpp                                          //
//  Developer:   Phillip Sharpless                                            //
//  Create date: 07/24/2007                                                   //
//                                                                            //
////////////////////////////////////////////////////////////////////////////////
//                                                                            //
//  Description: This is the class implementation for the slot gut of the     //
//               Australian Slot machine called Funky Monkey                  //
//                                                                            //
////////////////////////////////////////////////////////////////////////////////
//        Copyright (c) 2007 - Realtime Gaming.  All rights reserved.         //
////////////////////////////////////////////////////////////////////////////////

#include "FunkyMonkeyGuts.h"

////////////////////////////////////////////////////////////////////////////
/// FunkyMonkeyGuts::m_aLineWinSpinResults
///
/// This array is used to quickly get the SpinResult for the number of
/// icons.  It is indexed by wheel icon and then count.  So to get the
/// SpinResult for 5 Jeeps, use the following code snippet:
///
///		SpinResult result = m_aLineWinSpinResults[ ICON_JEEP ][ 5 ];
///
/// In this example result would be set to EVT_FIVE_JEEP.
////////////////////////////////////////////////////////////////////////////
FunkyMonkeyGuts::SpinResult FunkyMonkeyGuts::m_aLineWinSpinResults[][6] = 
{
// Number of icons:					0				1				2						3						4						5
/* ICON_NINE */			{	EVT_LOSER,		EVT_LOSER,		EVT_LOSER,			EVT_THREE_NINE,			EVT_FOUR_NINE,			EVT_FIVE_NINE		},
/* ICON_TEN */			{	EVT_LOSER,		EVT_LOSER,		EVT_LOSER,			EVT_THREE_TEN,			EVT_FOUR_TEN,			EVT_FIVE_TEN		},
/* ICON_JACK */			{	EVT_LOSER,		EVT_LOSER,		EVT_LOSER,			EVT_THREE_JACK,			EVT_FOUR_JACK,			EVT_FIVE_JACK		},
/* ICON_QUEEN */		{	EVT_LOSER,		EVT_LOSER,		EVT_LOSER,			EVT_THREE_QUEEN,		EVT_FOUR_QUEEN,			EVT_FIVE_QUEEN		},
/* ICON_KING */			{	EVT_LOSER,		EVT_LOSER,		EVT_LOSER,			EVT_THREE_KING,			EVT_FOUR_KING,			EVT_FIVE_KING		},
/* ICON_ACE */			{	EVT_LOSER,		EVT_LOSER,		EVT_LOSER,			EVT_THREE_ACE,			EVT_FOUR_ACE,			EVT_FIVE_ACE		},
/* ICON_GOLD_ALBUM */	{	EVT_LOSER,		EVT_LOSER,		EVT_TWO_GOLD_ALBUM,	EVT_THREE_GOLD_ALBUM,	EVT_FOUR_GOLD_ALBUM,	EVT_FIVE_GOLD_ALBUM },
/* ICON_JEEP */			{	EVT_LOSER,		EVT_LOSER,		EVT_TWO_JEEP,		EVT_THREE_JEEP,			EVT_FOUR_JEEP,			EVT_FIVE_JEEP		},
/* ICON_BONGO */		{	EVT_LOSER,		EVT_LOSER,		EVT_TWO_BONGO,		EVT_THREE_BONGO,		EVT_FOUR_BONGO,			EVT_FIVE_BONGO		},
/* ICON_KEYBOARD */		{	EVT_LOSER,		EVT_LOSER,		EVT_TWO_KEYBOARD,	EVT_THREE_KEYBOARD,		EVT_FOUR_KEYBOARD,		EVT_FIVE_KEYBOARD	},
/* ICON_FUNKY_MONKEY */	{	EVT_LOSER,		EVT_LOSER,		EVT_LOSER,			EVT_LOSER,				EVT_LOSER,				EVT_LOSER			},
/* ICON_MONKEY */		{	EVT_LOSER,		EVT_LOSER,		EVT_LOSER,			EVT_LOSER,				EVT_LOSER,				EVT_LOSER			},
/* ICON_FUNKY */		{	EVT_LOSER,		EVT_ONE_FUNKY,	EVT_LOSER,			EVT_LOSER,				EVT_LOSER,				EVT_LOSER			},
/* ICON_BANANA */		{	EVT_LOSER,		EVT_LOSER,		EVT_LOSER,			EVT_LOSER,				EVT_LOSER,				EVT_LOSER			}
};

////////////////////////////////////////////////////////////////////////////
/// FunkyMonkeyGuts::m_aScatterWinSpinResults
///
/// This array is used to quickly get the SpinResult for the number of
/// icons.  It is indexed by icon count only, since Bananas are the only
/// scatter win.  So to get the SpinResult for 5 Bananas, use the
/// following code snippet:
///
///		SpinResult result = m_aScatterWinSpinResults[ 5 ];
///
/// In this example result would be set to EVT_FIVE_BANANA.
////////////////////////////////////////////////////////////////////////////
FunkyMonkeyGuts::SpinResult FunkyMonkeyGuts::m_aScatterWinSpinResults[6] = 
{
	// ICON_BANANA				Number of Icons
	EVT_LOSER,					// 0
	EVT_SCATTER_NEAR_MISS,		// 1
	EVT_TWO_BANANA,				// 2
	EVT_THREE_BANANA,			// 3
	EVT_FOUR_BANANA,			// 4
	EVT_FIVE_BANANA				// 5
};

////////////////////////////////////////////////////////////////////////////
/// @fn 	 FunkyMonkeyGuts::FunkyMonkeyGuts()   
///
/// This is the default constructor for the class.
////////////////////////////////////////////////////////////////////////////
FunkyMonkeyGuts::FunkyMonkeyGuts()
{
	// If this doesn't compile it means that someone probably added
	// another icon type but did not update the
	// m_aLineWinSpinResults array. 
	C_ASSERT( countof( m_aLineWinSpinResults ) == NUM_VALID_ICONS );

}

////////////////////////////////////////////////////////////////////////////
/// @fn 	 FunkyMonkeyGuts::~FunkyMonkeyGuts()
///
/// Destructor for FunkyMonkeyGuts. 
////////////////////////////////////////////////////////////////////////////
FunkyMonkeyGuts::~FunkyMonkeyGuts()
{
}

////////////////////////////////////////////////////////////////////////////
/// @fn 	 FunkyMonkeyGuts::classifyStops()   
///
/// @param   stops      The array of stop positions for the line.
/// @param   bet        The value of the line bet.
/// @param   line       The index of the line.  
///
/// This helper method is used to calculate the payout on a line.
////////////////////////////////////////////////////////////////////////////
void FunkyMonkeyGuts::classifyStops( SpinLine* spinline, uint8* stops, uint8 bet, uint8 line,
	uint8& rtnresult, uint8& multiplier )
{
	// Start with the icon on the first reel of the payline
	uint8 testIcon = getIcon( getGameModeId(), 0, stops[0] )->iconid;

	// Declare a local variable to track whether or not we found a substitution.
	bool bFMSubstitution = false;
	bool bFSubstitution = false;
	bool bMSubstitution = false;

	// See what the spin result is if we used any Funky Monkeys as substitution symbols (or Funkys/Monkeys in free spin mode)
	rtnresult = getLineWinResultWithPossibleSubstitution( stops, testIcon, bFMSubstitution, bFSubstitution, bMSubstitution);

	// If we did actually have a substitution and a winning spin...
	if (bFMSubstitution && rtnresult != EVT_LOSER )
	{
		// then add a note
		spinline->addNote( NOTE_FUNKY_MONKEY_SUBSTITUTION );
	}
	if (bFSubstitution && rtnresult != EVT_LOSER )
	{
		// then add a note
		spinline->addNote( NOTE_FUNKY_SUBSTITUTION );
	}
	if (bMSubstitution && rtnresult != EVT_LOSER )
	{
		// then add a note
		spinline->addNote( NOTE_MONKEY_SUBSTITUTION );
	}

	//if we had a Funky on reel 1 but it did not serve as a substitution for a larger payout
	//we pay it's payline result instead
	if (getGameModeId() == MODE_FREE_SPIN && bFSubstitution && rtnresult == EVT_LOSER)
	{
		rtnresult = EVT_ONE_FUNKY;
	}

	if(getGameModeId() == MODE_FREE_SPIN)
	{
		multiplier = getFreeGameMultiplier();
	}
}

////////////////////////////////////////////////////////////////////////////
/// @fn 	 FunkyMonkeyGuts::SpinResult FunkyMonkeyGuts::getLineWinResultWithPossibleSubstitution( uint8* stops, uint8 testIcon, bool& bSubstitution ) const
///
/// @param   stops				The array of stop positions for the line.
/// @param   testIcon			The icon we are looking for, typically the first icon on the line.
/// @param   bFMSubstitution	An output parameter that is set if a Funky Monky subsitution is used.
/// @param   bFSubstitution		An output parameter that is set if a Funky subsitution is used.
/// @param   bMSubstitution		An output parameter that is set if a Monkey subsitution is used.
///
/// @return	 SpinResult				A payout from the SpinResult enumeration.
///
/// This helper method is used to calculate the line win payout, including possible substitution.
////////////////////////////////////////////////////////////////////////////
FunkyMonkeyGuts::SpinResult FunkyMonkeyGuts::getLineWinResultWithPossibleSubstitution(
	uint8* stops, uint8 testIcon, bool& bFMSubstitution, bool& bFSubstitution, bool& bMSubstitution) const
{
	// Initialize the out parameter
	bFMSubstitution = false;
	bFSubstitution = false;
	bMSubstitution = false;

	// Loop through the remaining reels until the streak is over
	for ( int i = 1; i < getReelCount(); i++ )
    {
		// See what this icon is.
		uint8 currentIcon = getIcon( getGameModeId(), i, stops[i] )->iconid;

		// If the current icon is a Funky Monkey
		if ( currentIcon == ICON_FUNKY_MONKEY )
		{
			// then use it as a substitution symbol and continue on.
			bFMSubstitution = true;
			continue;
		}
		// If the current icon is a Funky and in free spins mode
		else if ( currentIcon == ICON_FUNKY && getGameModeId() == MODE_FREE_SPIN)
		{
			// then use it as a substitution symbol and continue on.
			bFSubstitution = true;
			continue;
		}
		// If the current icon is a Monkey and in free spins mode
		else if ( currentIcon == ICON_MONKEY && getGameModeId() == MODE_FREE_SPIN)
		{
			// then use it as a substitution symbol and continue on.
			bMSubstitution = true;
			continue;
		}

		// Otherwise if the test icon is an Funky Monkey
		else if ( testIcon == ICON_FUNKY_MONKEY )
		{
			// then we still have a substitution.  In this case the line
			// started out with the substitution symbol.
			bFMSubstitution = true;

			// We have now found our first icon that is not a substitution symbol.
			// All of the rest of the icons will have to match this one now for the
			// streak to continue, so change our test icon.
			testIcon = currentIcon;
			continue;
		}

		// Otherwise if the test icon is an Funky and in free spins mode
		else if( testIcon == ICON_FUNKY &&  getGameModeId() == MODE_FREE_SPIN)
		{
			// then we still have a substitution.  In this case the line
			// started out with the substitution symbol.
			bFSubstitution = true;

			// We have now found our first icon that is not a substitution symbol.
			// All of the rest of the icons will have to match this one now for the
			// streak to continue, so change our test icon.
			testIcon = currentIcon;
			continue;
		}

		// If the current icon is not the test icon...
        if ( currentIcon != testIcon )
        {
			// then the streak is over so break out of the loop
			break;
        }
    }

	// Use the array to look up the spin result for i testIcons.
	return lookupLineWinSpinResult( testIcon, i );
}

////////////////////////////////////////////////////////////////////////////
/// @fn 	 FunkyMonkeyGuts::classifyScatters( SlotSpin* spin )
///
/// @param   spin       Pointer to the spin with stop data.
///
/// This is an override of the framework method.  
/// It determines if the current spin merits free spins or scatter payments.
////////////////////////////////////////////////////////////////////////////
void FunkyMonkeyGuts::classifyScatters( SlotSpin* spin )
{	
	// Try for the Random Progressive Jackpot
	tryForJackpot(spin);

	int32 nScatterPositions = 0;

	SpinResult srResult = getScatterWinResult(spin, nScatterPositions);

	// Add the scatter, if any.
	evaluateScatterWin(spin, srResult, nScatterPositions);

	// Now see if they triggered the feature.
	evaluateFeatureTrigger( spin, srResult, nScatterPositions );
}

////////////////////////////////////////////////////////////////////////////
/// @fn		FunkyMonkeyGuts::tryForJackpot()
///
///	This function tries to win the jackpot and adds the scatter if we hit
/// the jackpot.
////////////////////////////////////////////////////////////////////////////
void FunkyMonkeyGuts::tryForJackpot( SlotSpin* spin )
{
	// If we are in the first paid spin (NOT a bonus spin)...
	if ( getGameModeId() == MODE_NORMAL ) 
	{
		// then try for the Random Progressive Jackpot
		// If we hit the jackpot...
		if ( getRandomProgressiveJackpotResult( spin->getSpinLineCount() ) || 
			m_bForceRandomJackpot)
		{
			// then add a jackpot scatter to the spin.
			spin->addScatter( 0, getPayout( EVT_RANDOM_PROGRESSIVE_WINNER )->payoutAmount, 
				1, true, EVT_RANDOM_PROGRESSIVE_WINNER, 0 );
		}
    }
}

////////////////////////////////////////////////////////////////////////////
/// @fn		FunkyMonkeyGuts::getScatterWinResult()
///
/// @param   spin				The spin we are evaluating.
/// @param   scatterPositions	An output parameter that contains a bit mask for the icon positions.
///
/// @return	 SpinResult			A payout from the SpinResult enumeration.
///
///	This function looks for scatter wins.
////////////////////////////////////////////////////////////////////////////
FunkyMonkeyGuts::SpinResult FunkyMonkeyGuts::getScatterWinResult( SlotSpin* spin, int32& scatterPositions) const
{
	// Keep up with how many scatter icons we found.
	uint8 numScatterIcons = 0;

	// Loop through each reel.
  	for( int reel = 0; reel < getReelCount(); reel++ )
	{
		// The position bit for the current icon in case it needs to be stored in a mask.
		uint8 position = reel;

		// Keep up with whether or not we found a scatter icon on this reel.
		bool bFoundScatterIconOnReel = false;

		// Loop through each row in the wheel house.
		for( int row = 0; row < 3; row++ )
		{
			// Get the current stop.
			uint8 stop = relativeWheelPosition( getGameModeId(), reel, spin->getStops()[ reel ], row );

			// Get the icon for the current stop.
			uint8 currentIcon = getIcon( getGameModeId(), reel, stop )->iconid;
			
			// If the icon is a Banana...
			if ( currentIcon == ICON_BANANA )
			{
				// Set the flag so that we know we found a scatter icon on this reel.
				bFoundScatterIconOnReel = true;
			}

			// If we are in free game mode, Funky and Monkey symbols serve as substitutes
			// including for scattered bananas
			else if (getGameModeId() == MODE_FREE_SPIN &&
				     (currentIcon == ICON_FUNKY || currentIcon == ICON_MONKEY))
			{
				bFoundScatterIconOnReel = true;
			}

			// If we found a scatter icon...
			if ( bFoundScatterIconOnReel )
			{
				// then increase the scatter icon count
				numScatterIcons++;

				// Set the bit in the position mask for this stop.
				scatterPositions |= 1 << position;

				// Break out now.
				break;
			}

			// Move to the next bit in the position mask.
            position += 5;
		}
	}

	// Use the array to look up the spin result in the current game mode.
	return lookupScatterWinSpinResult( numScatterIcons );
}

////////////////////////////////////////////////////////////////////////////
/// @fn 	 FunkyMonkeyGuts::evaluateScatterWin()
///
/// This function determines if the current spin wins a scatter payment and
/// adds the scatter.
////////////////////////////////////////////////////////////////////////////
void FunkyMonkeyGuts::evaluateScatterWin( SlotSpin* spin, SpinResult srScatterResult, int32 scatterPositions)
{
	// If we got the scatter win (or almost got it)
	if ( srScatterResult != EVT_LOSER )
	{
		// Get the base payout for the scatter.
		int32 nPayout = getPayout( srScatterResult )->payoutAmount;
		
		// Scatters payout based on the total bet, so factor in the number of bet lines.
		nPayout *= spin->getSpinLineCount();

		int32 scatterMultiplier = 1;

		if(getGameModeId() == MODE_FREE_SPIN)
		{
			scatterMultiplier = getFreeGameMultiplier();
		}

        // mbolanos.    07-12-04    20482: The TotalWin should be increased by the payout*scatterMultiplier. 
        // Add the payout to the total win for this spin.
		increaseTotalWin( nPayout * scatterMultiplier);


		// Add a scatter to the spin.
		spin->addScatter( scatterPositions, nPayout * getBetPerLine(), scatterMultiplier, false, srScatterResult, 0 );
	}

	if(getGameModeId() == MODE_FREE_SPIN)
	{
		checkForMultipliersWon(spin, true);
	}
}

////////////////////////////////////////////////////////////////////////////
/// @fn 	 FunkyMonkeyGuts::evaluateFeatureTrigger()
///
/// This function determines if the current spin triggers the free game
///	feature and adds the scatter.
////////////////////////////////////////////////////////////////////////////
void FunkyMonkeyGuts::evaluateFeatureTrigger( SlotSpin* spin, SpinResult srScatterResult, int32 scatterPositions )
{
	//the feature is triggered when Funky appears on reel 0 and Monkey appears on reel 1
	//in normal game mode only, it cannot be retriggered
	if ( getGameModeId() != MODE_NORMAL) 
	{
		return;
	}

	bool bFunkyOnReel0 = false;
	bool bMonkeyOnReel4 = false;

	// Loop through each reel.
  	for( int reel = 0; reel < getReelCount(); reel++ )
	{
		// Loop through each row in the wheel house.
		for( int row = 0; row < 3; row++ )
		{
			// Get the current stop.
			uint8 stop = relativeWheelPosition( getGameModeId(), reel, spin->getStops()[ reel ], row );

			// Get the icon for the current stop.
			uint8 currentIcon = getIcon( getGameModeId(), reel, stop )->iconid;
			
			// If the icon is a Funky
			if ( currentIcon == ICON_FUNKY && reel == 0)
			{
				bFunkyOnReel0 = true;
			}

			// If the icon is a Monkey
			if ( currentIcon == ICON_MONKEY && reel == 4)
			{
				bMonkeyOnReel4 = true;
			}
		}
	}

	if(bFunkyOnReel0 && bMonkeyOnReel4)
	{
		// Add a scatter to the spin for the feature trigger.
		spin->addScatter( scatterPositions, 0, 0, false, EVT_FUNKY_MUSIC_FEATURE, 0 );

		initializePickFeatureGame();
	}

	return;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////
/// @fn 	 FunkyMonkeyGuts::checkForMultipliersWon( SlotSpin* spin )
///
/// @param   spin       Pointer to the spin with stop data.
///
/// This method checks a spin to see if additional multipliers were won during a free spin and 
/// either increments the multiplier or adds the scatters indicating the multiplier wins to the spin
////////////////////////////////////////////////////////////////////////////////////////////////////////
void FunkyMonkeyGuts::checkForMultipliersWon(SlotSpin *spin, bool AddScatters)
{
	bool bFunkyOnReel0 = false;
	bool bMonkeyOnReel4 = false;

	// Loop through each reel.
  	for( int reel = 0; reel < getReelCount(); reel++ )
	{
		// Loop through each row in the wheel house.
		for( int row = 0; row < 3; row++ )
		{
			// Get the current stop.
			uint8 stop = relativeWheelPosition( getGameModeId(), reel, spin->getStops()[ reel ], row );

			// Get the icon for the current stop.
			uint8 currentIcon = getIcon( getGameModeId(), reel, stop )->iconid;
			
			// If the icon is a Funky
			if ( currentIcon == ICON_FUNKY && reel == 0)
			{
				bFunkyOnReel0 = true;
			}

			// If the icon is a Monkey
			if ( currentIcon == ICON_MONKEY && reel == 4)
			{
				bMonkeyOnReel4 = true;
			}
		}
	}

	//if the spin is going to bring us up to 10x on the multiplier, we need to record it
	if(getFreeGameMultiplier() == 8 && bFunkyOnReel0 && bMonkeyOnReel4)
		m_nHit10XMultiplier = 2;

	if(getFreeGameMultiplier() == 9 && (bFunkyOnReel0 || bMonkeyOnReel4))
		m_nHit10XMultiplier = 1;


	//if we won multipliers we need to incremeant the multiplier counter before the spin
	//line results are calculated, but need to add the actual scatter results indicating the
	//multiplier win later
	if(bFunkyOnReel0 && getFreeGameMultiplier() < 10)
	{
		if(AddScatters)
		{
			spin->addScatter( 0, 0, 0, false, EVT_MULTIPLIER_WON, 0 );
		}
		else
		{
			setFreeGameMultiplier(getFreeGameMultiplier() + 1);
		}
	}
	if(bMonkeyOnReel4 && getFreeGameMultiplier() < 10)
	{
		if(AddScatters)
		{
			spin->addScatter( 0, 0, 0, false, EVT_MULTIPLIER_WON, 0 );
		}
		else
		{
			setFreeGameMultiplier(getFreeGameMultiplier() + 1);
		}
	}

	//if we are at 10x on the multiplier we need a way it on this spin
	//and thus need to add scatter messages about winning multipliers or if we have already
	//been at 10x and thus do not need to add any messages
	if(AddScatters && m_nHit10XMultiplier > 0 && getFreeGameMultiplier() == 10)
	{
		if(m_nHit10XMultiplier == 2)
		{
			spin->addScatter( 0, 0, 0, false, EVT_MULTIPLIER_WON, 0 );
			spin->addScatter( 0, 0, 0, false, EVT_MULTIPLIER_WON, 0 );
		}
		else if(m_nHit10XMultiplier == 1)
		{
			spin->addScatter( 0, 0, 0, false, EVT_MULTIPLIER_WON, 0 );
		}

		m_nHit10XMultiplier = 0;
	}	

}

////////////////////////////////////////////////////////////////////////////
/// @fn 	 FunkyMonkeyGuts::isValidGuts()   
///
/// @return	 bool8      True if gut is valid.    
///
/// This helper method is used to see if the current gut ID is valid.
////////////////////////////////////////////////////////////////////////////
bool8 FunkyMonkeyGuts::isValidGuts()
{
	// Make sure that the m_aLineWinSpinResults array has an entry for each reel,
	// and an additional entry in case there are 0 icons on the line.
	// Note:  we can't do this in the constructor because we have to call
	// getReelCount.
	ASSERT( countof( m_aLineWinSpinResults[0] ) == getReelCount( MODE_NORMAL ) + 1 );

	// Make sure that the m_aScatterWinSpinResults array has an entry for each reel,
	// and an additional entry in case there are 0 icons on the line.
	ASSERT( countof( m_aScatterWinSpinResults ) == getReelCount( MODE_NORMAL ) + 1 );

	// Now make sure that both game modes have the same number of reels.
	ASSERT( getReelCount( MODE_NORMAL ) == getReelCount( MODE_FREE_SPIN ) );

	// Finally just check to see if the gut is within the right range.
    return ( getGutsId() >= 2200 && getGutsId() < 2300 );
}

////////////////////////////////////////////////////////////////////////////
/// @fn 	 FunkyMonkeyGuts::getGameModeMultiplierAndNoteId( uint8& nGameModeMultiplier, uint8& nNoteId )
///
/// @param   nGameModeMultiplier	The multiplier for the game mode.
/// @param   nNoteId				The note ID for the game mode.
///
/// This helper method is used to get the game mode multiplier and note ID.
////////////////////////////////////////////////////////////////////////////
void FunkyMonkeyGuts::getGameModeMultiplierAndNoteId( uint8& nGameModeMultiplier, uint8& nNoteId )
{
	// If we are in free spin mode...
    if ( getGameModeId() == MODE_FREE_SPIN )
    {
		// just defer to the base class.
        SlotGuts::getGameModeMultiplierAndNoteId(nGameModeMultiplier, nNoteId );
    }
	// Otherwise...
    else
    {
		// just defer to the base class.
        SlotGuts::getGameModeMultiplierAndNoteId(nGameModeMultiplier, nNoteId );
    }
}

///////////////////////////////////////////////////////////////////////////////////////
/// @fn 	 FunkyMonkeyGuts::processClientData(uint8 bonusGameType, int32 bonusDataCount, int32 *bonusData)
///
/// @param   bonusGameType      The current type of bonus game.
/// @param   bonusDataCount     The number of integers passed from the client.
/// @param   bonusData			An array of integers passed from the client.
/// 
/// This is an override of the framework method.
/// 
///////////////////////////////////////////////////////////////////////////////////////
bool8 FunkyMonkeyGuts::processClientData(uint8 bonusGameType, int32 bonusDataCount, int32 *bonusData)
{
	// Start by assuming that we do not need to reply to this message
	bool8 bReply = false;

	if ( getBonusGameState() == BONUS_GAME_FUNKY_MUSIC )
	{
		if ( bonusGameType == BONUS_GAME_FUNKY_MUSIC )
		{
			if ( bonusDataCount == 1 )
			{
				int32 nPosition = *bonusData;

				if ( nPosition >= 0 && nPosition < MAX_POSITIONS )
				{
					// The state will change, so a reply is required
					bReply = true;

					if ( getFeatureGamePickPosition() < 0) //haven't picked yet
					{
						int32 nFreeSpins;

						nFreeSpins = getRandomValueFromProbabilityTable(PROBABILITY_PERFORMER_PRIZE);

						if(m_bDebugSpinsMode)
						{
							if(g_nDebugFreeSpins != WIN_TYPE_NONE)
							{
								nFreeSpins = g_nDebugFreeSpins;
							}
						}

						addBonusGame(0, 0, 0, 0, 0, nFreeSpins);

						setFeatureGamePickPosition( nPosition );
						setFeatureGameFreeSpinsWon( nFreeSpins );

						m_feature->setLevel( 1 );

						incrementFreeSpins(nFreeSpins);

						setGameModeId(MODE_FREE_SPIN);
		
						setBonusGameState(BONUS_GAME_NONE);
						setGameCanComplete(true);

						if(m_bDebugSpinsMode)
						{
							//reset debug data
							g_nDebugFreeSpins = WIN_TYPE_NONE;
						}
					}
				}
				else
				{
					// Invalid performer position.  Log it.
				}
			}
			else
			{
				// Too many data items.  Log it.
			}
		}
		else
		{
			// Invalid bonus game type from client.  Log it.
		}
	}
	else
	{
		//  Invalid state to receive this message.  Log it.
	} 

	return bReply;
}

////////////////////////////////////////////////////////////////////////////
/// @fn 	 FunkyMonkeyGuts::hasSavedGames()   
///
/// This helper method is a framework override that indicates this machine can have saved games.
////////////////////////////////////////////////////////////////////////////
bool8 FunkyMonkeyGuts::hasSavedGames()
{
	return true;
}

////////////////////////////////////////////////////////////////////////////
/// @fn 	 FunkyMonkeyGuts::initializePickFeatureGame() 
///
/// This is a helper method to initialize the Funky Music feature game.
////////////////////////////////////////////////////////////////////////////
void FunkyMonkeyGuts::initializePickFeatureGame()
{
	setGameCanComplete(false);
	setBonusGameState(BONUS_GAME_FUNKY_MUSIC);
	setFeatureGamePickPosition(-1);
	setFeatureGameFreeSpinsWon(WIN_TYPE_NONE);
	setFreeGameMultiplier(1);
	m_nHit10XMultiplier = 0;
}

////////////////////////////////////////////////////////////////////////////
/// @fn 	 FunkyMonkeyGuts::setFeatureGamePickPosition(int32 nPick)
///
/// @param   nPick  The index of the performer selected during the feature game.
///
/// This is an accessor mutator method to set the index of the performer selected.
////////////////////////////////////////////////////////////////////////////
void FunkyMonkeyGuts::setFeatureGamePickPosition(int32 nPick)
{
	m_subGame->update(SGDIDX_PERFORMER_SELECTED, nPick);
}

////////////////////////////////////////////////////////////////////////////
/// @fn 	 FunkyMonkeyGuts::getFeatureGamePickPosition() const 
///
/// @return  int32   The index of the performer selected during the feature game.
///
/// This is an accessor mutator method to get the index of the performer selected.
////////////////////////////////////////////////////////////////////////////
int32 FunkyMonkeyGuts::getFeatureGamePickPosition() const
{
	return m_subGame->getData(SGDIDX_PERFORMER_SELECTED);
}

////////////////////////////////////////////////////////////////////////////
/// @fn 	 FunkyMonkeyGuts::setFeatureGameFreeSpinsWon(int32 nFreeSpins)
///
/// @param   nFreeSpins  The number of free spins won.
///
/// This is a mutator to set the number of free spins won during the feature game.
////////////////////////////////////////////////////////////////////////////
void FunkyMonkeyGuts::setFeatureGameFreeSpinsWon(int32 nFreeSpins)
{
	m_subGame->update(SGDIDX_FREE_GAMES_AWARDED, nFreeSpins);
}

////////////////////////////////////////////////////////////////////////////
/// @fn 	 FunkyMonkeyGuts::getFeatureGameFreeSpinsWon() const 
///
/// @return  int32   The number of free spins won.
///
/// This is a mutator to get the number of free spins won during the feature game.
////////////////////////////////////////////////////////////////////////////
int32 FunkyMonkeyGuts::getFeatureGameFreeSpinsWon() const
{
	return m_subGame->getData(SGDIDX_FREE_GAMES_AWARDED);
}

////////////////////////////////////////////////////////////////////////////
/// @fn 	 FunkyMonkeyGuts::setFreeGameMultiplier(int32 nMultiplier)
///
/// @param   nMultiplier  The multiplier for the free spins.
///
/// This is an accessor mutator method to set the current multiplier for the free spins.
////////////////////////////////////////////////////////////////////////////
void FunkyMonkeyGuts::setFreeGameMultiplier(int32 nMultiplier)
{
	m_subGame->update(SGDIDX_FREE_GAMES_MULTIPLIER, nMultiplier);
}

////////////////////////////////////////////////////////////////////////////
/// @fn 	 FunkyMonkeyGuts::getFreeGameMultiplier() const 
///
/// @return  int32   The current multiplier for the free games.
///
/// This is an accessor mutator method to get the current multiplier for the free spins.
////////////////////////////////////////////////////////////////////////////
int32 FunkyMonkeyGuts::getFreeGameMultiplier() const
{
	return m_subGame->getData(SGDIDX_FREE_GAMES_MULTIPLIER);
}

////////////////////////////////////////////////////////////////////////////
/// @fn 	 FunkyMonkeyGuts::preSpinClassifyResults( SlotSpin* spin )
///
/// @param   spin       Pointer to the spin with stop data.
///
/// This method is overridden in debug spins mode to alter the reel stops.
////////////////////////////////////////////////////////////////////////////
void FunkyMonkeyGuts::preSpinClassifyResults( SlotSpin* spin )
{
	SlotGuts::preSpinClassifyResults(spin);

	if(getGameModeId() == MODE_FREE_SPIN)
	{
		checkForMultipliersWon(spin, false);
	}
	else
	{
		setFreeGameMultiplier(0);
	}

	return;
}

////////////////////////////////////////////////////////////////////////////
/// Debug spins functionality, can control how many free spins are won
/// during the Funky Music feature
////////////////////////////////////////////////////////////////////////////
void FunkyMonkeyGuts::initDebugFunctionMap()
{
	SlotGuts::initDebugFunctionMap();

	addDebugStopFunction( EVT_DEBUG_FUNCTION_8_SPINS, 
		new DebugStopFunc<FunkyMonkeyGuts>(this, &FunkyMonkeyGuts::DebugFeature_8_Spins) );

	addDebugStopFunction( EVT_DEBUG_FUNCTION_10_SPINS, 
		new DebugStopFunc<FunkyMonkeyGuts>(this, &FunkyMonkeyGuts::DebugFeature_10_Spins) );

	addDebugStopFunction( EVT_DEBUG_FUNCTION_12_SPINS, 
		new DebugStopFunc<FunkyMonkeyGuts>(this, &FunkyMonkeyGuts::DebugFeature_12_Spins) );

	addDebugStopFunction( EVT_DEBUG_FUNCTION_15_SPINS, 
		new DebugStopFunc<FunkyMonkeyGuts>(this, &FunkyMonkeyGuts::DebugFeature_15_Spins) );

	addDebugStopFunction( EVT_DEBUG_FUNCTION_20_SPINS, 
		new DebugStopFunc<FunkyMonkeyGuts>(this, &FunkyMonkeyGuts::DebugFeature_20_Spins) );

	addDebugStopFunction( EVT_DEBUG_FUNCTION_25_SPINS, 
		new DebugStopFunc<FunkyMonkeyGuts>(this, &FunkyMonkeyGuts::DebugFeature_25_Spins) );
}

int FunkyMonkeyGuts::g_nDebugFreeSpins = WIN_TYPE_NONE;

void FunkyMonkeyGuts::DebugFeature_8_Spins( SlotSpin *spin )
{
    g_nDebugFreeSpins = WIN_TYPE_8_FREE_GAMES;
}

void FunkyMonkeyGuts::DebugFeature_10_Spins( SlotSpin *spin )
{
    g_nDebugFreeSpins = WIN_TYPE_10_FREE_GAMES;
}

void FunkyMonkeyGuts::DebugFeature_12_Spins( SlotSpin *spin )
{
    g_nDebugFreeSpins = WIN_TYPE_12_FREE_GAMES;
}

void FunkyMonkeyGuts::DebugFeature_15_Spins( SlotSpin *spin )
{
    g_nDebugFreeSpins = WIN_TYPE_15_FREE_GAMES;
}

void FunkyMonkeyGuts::DebugFeature_20_Spins( SlotSpin *spin )
{
    g_nDebugFreeSpins = WIN_TYPE_20_FREE_GAMES;
}

void FunkyMonkeyGuts::DebugFeature_25_Spins( SlotSpin *spin )
{
    g_nDebugFreeSpins = WIN_TYPE_25_FREE_GAMES;
}